<?php
/**
 * crcl/base
 *
 * Copyright 2013 CRCL - André Sabosch - info@crcl.de
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

namespace crcl\base\structure\field;

interface IConcrete
{
  public function set($value);
  public function get();
  public function __toString();
  public function getRaw();
  public function getLast();
  public function getRawLast();
  public function commit();
  public function fromGet($key);
  public function fromPost($key);
  public function process($sUri);
  public function filter($sFilter);
  public function validate($sValidator);
  public function asInteger();
  public function asBoolean();
  public function asString();
}

abstract class Base implements IConcrete
{
  public function __construct($sUri)
  {
    $this->sUri = $sUri;
    $oUri = \crcl\base\Resource::reader($sUri);
    $aParams = $oUri->getParams();
    if (isset($aParams[\crcl\base\structure\Field::CONFIG_DEFAULT]))
    {
      $this->default = $aParams[\crcl\base\structure\Field::CONFIG_DEFAULT];
      $this->value_last = $this->default;
      $this->value = $this->default;
    }
    if (isset($aParams[\crcl\base\structure\Field::CONFIG_PROCESSORS]))
    {
      $sProcessors = \urldecode($aParams[\crcl\base\structure\Field::CONFIG_PROCESSORS]);
      $this->aProcessors = \explode(',', $sProcessors);
    }
  }

  public function set($value)
  {
    if (!\is_null($value))
    {
      $this->check($value);
    }
    $this->value_last = $this->value;
    $this->value = $value;
    $this->bDirty = \TRUE;

    return $this->get();
  }

  public function get()
  {
    $value = $this->executeProcessors($this->value);
    return $value;
  }

  public function getRaw()
  {
    return $this->value;
  }

  public function getDefault()
  {
    return $this->default;
  }

  public function getLast()
  {
    $value = $this->executeProcessors($this->value_last);
    return $value;
  }

  public function getRawLast()
  {
    return $this->value_last;
  }

  public function commit()
  {
    $this->bDirty = \FALSE;
    $this->value_last = $this->value;
  }

  public function rollback()
  {
    $this->bDirty = \FALSE;
    $this->value = $this->value_last;
  }

  public function isDirty()
  {
    return $this->bDirty;
  }

  public function __toString()
  {
    return self::get();
  }

  public function fromGet($key)
  {
    $this->fromX($key, $_GET);
    return $this;
  }

  public function fromPost($key)
  {
    $this->fromX($key, $_POST);
    return $this;
  }

  public function process($sUri)
  {
    $this->value = $this->doProcess($this->value, $sUri);
  }

  public function filter($sFilter)
  {
    $oFilter = \crcl\base\Filter::getInstance($sFilter);
    $this->value = $oFilter->filter($this->value);
  }

  public function validate($sValidator)
  {
    $oValidator = \crcl\base\Validator::getInstance($sValidator);
    $oValidator->test($this->value);
  }

  public function asInteger()
  {
    $oField = new ContainerInteger($this->sUri);

    $oField->set($this->value_last);
    $oField->commit();
    $oField->set($this->value);

    return $oField;
  }

  public function asBoolean()
  {
    $oField = new ContainerBoolean($this->sUri);
    $oField->set($this->value_last);
    $oField->commit();
    $oField->set($this->value);

    return $oField;
  }

  public function asString()
  {
    $oField = new ContainerString($this->sUri);
    $oField->set($this->value_last);
    $oField->commit();
    $oField->set($this->value);

    return $oField;
  }

  /** PROTECTED ***********************************************************************************/

  protected $sUri;
  protected $value = \NULL;
  protected $value_last = \NULL;
  protected $default = \NULL;

  protected $aProcessors = array();

  protected $bDirty = \FALSE;

  //@codeCoverageIgnoreStart
  abstract protected function check($value);
  //@codeCoverageIgnoreEnd

  /** PRIVATE *************************************************************************************/

  private function executeProcessors($value)
  {
    foreach ($this->aProcessors as $sProcessor)
    {
      $value = $this->doProcess($value, $sProcessor);
    }
    return $value;
  }

  private function doProcess($value, $sUri)
  {
    $sScheme = \crcl\base\resource\Utils::getScheme($sUri);
    switch ($sScheme)
    {
      case 'filter':
        $sFilter = \substr($sUri, 9);  //strlen('filter://')
        $oFilter = \crcl\base\Filter::getInstance($sFilter);
        $value = $oFilter->filter($value);
        break;
      case 'validator':
        $sValidator = \substr($sUri, 12);  //strlen('validator://')
        $oValidator = \crcl\base\Validator::getInstance($sValidator);
        $oValidator->test($value);
        break;
      default:
        throw new Exception('Invalid processor.', Exception::ERROR_INVALIDPROCESSOR,
          array('sUri' => $sUri));
    }
    return $value;
  }

  private function fromX($key, $aX)
  {
    if (isset($aX[$key]))
    {
      $this->set($aX[$key]);
    }
    else
    {
      throw new Exception(
        'Value not found.',
        Exception::ERROR_VALUENOTFOUND,
        array('key' => $key,
          'from' => $aX));
    }
  }
}